在访问 k8s 服务时,有时会出现一直连不上的问题,我们可以通过分析 iptables 和抓包的方式观察报文是否正确到达。
Iptables 跟踪
设置如下,具体参考[1]:
1 | # Load the (IPv4) netfilter log kernel module |
这里我们以 k8s NodePort 类型的 service 为例,假如我们希望追踪 23741 端口的规则,设置如下:
1 | iptables -t raw -j TRACE -p tcp --dport 32741 -I PREROUTING 1 |
查看 /var/log/messages
中的追踪记录
为了查看规则,现在某个机器上 curl 一下主机的 32741 端口。
1 | raw:PREROUTING:policy:2 IN=enp0s3 OUT= MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.199.119 LEN=64 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=50995 DPT=32741 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) |
根据上面的图我们知道报文是按照 nat:PREROUTING
-> filter:FORWARD
-> nat:POSTROUTING
传输的。
按规则分析,先看第一条:
1 | nat:PREROUTING:rule:1 IN=enp0s3 OUT= MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.199.119 LEN=64 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=50995 DPT=32741 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) |
iptables 的 PREROUTING 如下:
1 | Chain PREROUTING (policy ACCEPT) |
可以看出所有报文都会匹配第一条规则,也就是 KUBE-SERVICES
, 也就是 trace 里的:
1 | nat:KUBE-SERVICES:rule:9 IN=enp0s3 OUT= MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.199.119 LEN=64 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=50995 DPT=32741 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) |
再看 iptables 的 KUBE-SERVICES
1 | Chain KUBE-SERVICES (2 references) |
很明显匹配的是 KUBE-NODEPORTS
, 也就是:
1 | TRACE: nat:KUBE-NODEPORTS:rule:1 IN=enp0s3 OUT= MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.199.119 LEN=64 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=50995 DPT=32741 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) |
iptables 的 KUBE-NODEPORTS 如下:
1 | Chain KUBE-NODEPORTS (1 references) |
先走第一个条 KUBE-MARK-MASQ
1 | TRACE: nat:KUBE-MARK-MASQ:rule:1 IN=enp0s3 OUT= MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.199.119 LEN=64 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=50995 DPT=32741 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) |
iptables 的 KUBE-MARK-MASQ 如下:
1 | Chain KUBE-MARK-MASQ (11 references) |
k8s 会给报文打上 0x4000
的标签, 打完标签后会返回,然后继续匹配 KUBE-NODEPORTS
的下一条规则。也就是 KUBE-SVC-4N57TFCL4MD7ZTDA
1 | TRACE: nat:KUBE-MARK-MASQ:return:2 IN=enp0s3 OUT= MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.199.119 LEN=64 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=50995 DPT=32741 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) MARK=0x4000 |
iptables 的 KUBE-SVC-4N57TFCL4MD7ZTDA 如下:
1 | Chain KUBE-SVC-4N57TFCL4MD7ZTDA (2 references) |
进入 KUBE-SEP-PJQYOXMI5CEBVECW
1 | TRACE: nat:KUBE-SEP-PJQYOXMI5CEBVECW:rule:2 IN=enp0s3 OUT= MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.199.119 LEN=64 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=50995 DPT=32741 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) MARK=0x4000 |
iptables 的 KUBE-SEP-PJQYOXMI5CEBVECW 如下:
1 | Chain KUBE-SEP-PJQYOXMI5CEBVECW (1 references) |
可以看到这里走的是 DNAT, 将报文中的目的地址换成了 92.168.3.4:80
, 也就是 k8s 服务对应 pod 的 ip 和端口号。
DNAT 完了之后会将报文发给 filter 表的 FORWARD 链。
1 | TRACE: filter:FORWARD:rule:1 IN=enp0s3 OUT=cni0 MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.3.4 LEN=64 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50995 DPT=80 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) MARK=0x4000 |
iptables 的 FORWARD 如下:
1 | Chain FORWARD (policy DROP) |
可以看到匹配第一条,进入 KUBE-FORWARD
1 | TRACE: filter:KUBE-FORWARD:rule:1 IN=enp0s3 OUT=cni0 MAC=08:00:27:63:c4:b1:f0:18:98:36:f6:c4:08:00 SRC=192.168.199.132 DST=192.168.3.4 LEN=64 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50995 DPT=80 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) MARK=0x4000 |
iptables 的 KUBE-FORWARD 如下:
1 | Chain KUBE-FORWARD (1 references) |
forward 完了之后会转给 iptables 的 nat 表的 POSTROUTING:
1 | TRACE: nat:POSTROUTING:rule:1 IN= OUT=cni0 SRC=192.168.199.132 DST=192.168.3.4 LEN=64 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50995 DPT=80 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) MARK=0x4000 |
iptables 的 POSTROUTING 如下:
1 | Chain POSTROUTING (policy ACCEPT) |
命中第一条,转给 KUBE-POSTROUTING
1 | TRACE: nat:KUBE-POSTROUTING:rule:1 IN= OUT=cni0 SRC=192.168.199.132 DST=192.168.3.4 LEN=64 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=TCP SPT=50995 DPT=80 SEQ=1677343889 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 OPT (020405B4010303050101080A08CB9A710000000004020000) MARK=0x4000 |
iptables 的 KUBE-POSTROUTING 如下:
1 | Chain KUBE-POSTROUTING (1 references) |
汇总一下,大概路线如下:
1 | --> [nat]PREROUTING |
清除追踪规则
查看规则 number
1
2
3
4
5
6
7
8$ sudo iptables -t raw -nL --line-number
Chain PREROUTING (policy ACCEPT)
num target prot opt source destination
1 TRACE tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:32741
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
1 TRACE tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:32741删除规则
上面查到的 number 是1
, 这里删除第一条规则:1
2$ sudo iptables -t raw -D PREROUTING 1
$ sudo iptables -t raw -D OUTPUT 1